/**
 *  JFTSTool - Java Full Text Search tool.
 *  Copyright (C) <2009>  <grupo JFTS>
 *  This program is free software: you can redistribute it and/or modify
 *	it under the terms of the GNU General Public License as published by
 *	the Free Software Foundation, either version 3 of the License, or
 *	(at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *  
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.jftstool;

import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import org.apache.log4j.Logger;
import org.apache.log4j.xml.DOMConfigurator;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriter.MaxFieldLength;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Searcher;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.jftstool.analyzers.AnalyzerManager;
import org.jftstool.analyzers.JFTSAnalyzer;
import org.jftstool.analyzers.exceptions.AnalyzerManagerException;
import org.jftstool.config.InitializeConfig;
import org.jftstool.config.JFTSConfiguration;
import org.jftstool.config.exceptions.JFTSConfigException;
import org.jftstool.exceptions.JFTSFrameworkException;
import org.jftstool.indexer.IJFTSIndexer;
import org.jftstool.indexer.JFTSIndexer;
import org.jftstool.indexer.exceptions.JFTSIndexerException;
import org.jftstool.result.JFTSResult;
import org.jftstool.resultOrder.ResultOrder;
import org.jftstool.searcher.IJFTSSearcher;
import org.jftstool.searcher.JFTSSearcher;
import org.jftstool.searcher.exceptions.JFTSSearcherException;
import org.jftstool.semantic.DBSemanticModel;
import org.jftstool.semantic.ISemanticModel;
import org.jftstool.semantic.JFTSHierarchy;
import org.jftstool.semantic.exceptions.JFTSSemanticException;

/**
 * @author grupo JFTS
 *
 */
public class JFTSFramework implements IJFTSIndexer, IJFTSSearcher {
	private JFTSConfiguration config = null;
	private InitializeConfig initConfig = null;
	private Map<String, IndexWriter> iws = null;
	private Searcher is;
	private IndexReader ir = null;
	private String configPath = null;
	private String logPath = null;	
	private JFTSIndexer indexer = null;
	private JFTSSearcher searcher = null;	
	private ISemanticModel sm = null;
	private static Logger logger = Logger.getLogger(JFTSFramework.class);	
	private static JFTSFramework instance = null;
	private boolean sessionStarted = false;
	
	private JFTSFramework(){ /* do nothing */ }
	
	public static JFTSFramework getJFTSInstance(){
		if (instance == null){
			instance = new JFTSFramework();
		}
		return instance;
	}
	
	
	/**
	 * Starts a session JFTS Tool. By default does not create the semantic model.
	 * @throws JFTSFrameworkException
	 */
	public void startSession() throws JFTSFrameworkException {
		startSession(false);
	}
	

	/**
	 * Starts a session JFTS Tool
	 * @param createModel If true and there is a model created, deleted and recreated. <br>
	 * If false, it loads the current model. 
	 * @throws JFTSFrameworkException
	 */
	public void startSession(boolean createModel) throws JFTSFrameworkException {
		try {
			if (logPath == null){
				DOMConfigurator.configure("resources/log4j/log4jConfig.xml");	
			}
			else{
				DOMConfigurator.configure(logPath);				
			}
			if (!sessionStarted){				
				logger.info("***JFTSTool - Initializing session***");
				
				config = (configPath == null) ? JFTSConfiguration.getInstance() : JFTSConfiguration.getInstance(configPath);
				initConfig = config.getInitConfig();
				
				sm = new DBSemanticModel(config);
				if (createModel){
					sm.createSemanitcModel();
				}
				sm.loadSemanicModel();
				
				boolean readyToIndex = initConfig.getReadyToIndex();
				boolean readyToSearch = initConfig.getReadyToSearch();			
	
				//IndexWriter
				if (readyToIndex){
					logger.info(">> Start indexWriters");				
					iws = new HashMap<String, IndexWriter>();
					_initializeIWS();
					logger.info("\t ready to index");								
				}
				
				//IndexSearcher
				if (readyToSearch){
					logger.info(">> Start indexSearcher");				
					_initializeIS();
					logger.info("\t ready to search");								
				}
			
				logger.info(">> Create indexer");			
				indexer = new JFTSIndexer(iws, sm);
				
				logger.info(">> Create searcher");			
				searcher = new JFTSSearcher(is, ir, sm);
				
				logger.info("***JFTSTool - Start session correctly***");
				sessionStarted = true;
			}
			else{
				logger.info("***The session has already been initialized previously***");				
			}
		}
		catch (JFTSConfigException e) {
			_closeSessionWithError();
			throw new JFTSFrameworkException(e.getMessage());			
		}
		catch (JFTSIndexerException e) {
			_closeSessionWithError();
			throw new JFTSFrameworkException(e.getMessage());			
		}
		catch (JFTSSemanticException e) {
			_closeSessionWithError();
			throw new JFTSFrameworkException(e.getMessage());			
		}
		catch (JFTSSearcherException e) {
			_closeSessionWithError();
			throw new JFTSFrameworkException(e.getMessage());			
		}
	}

	
	/**
	 * Closes a session JFTS Tool
	 * This means closing all IndexWriters.
	 * @param merge If equal to true, performs the merge of the auxiliary index.
	 * @param optimize If equal to true, it optimizes the main index at the end.
	 * @throws JFTSFrameworkException
	 */
	public void closeSession(boolean merge, boolean optimize) throws JFTSFrameworkException {
		logger.info("***JFTSTool - Starting close session***");
		
		logger.info(">> Closing IndexWriters");		
		//close indexWriters
		String[] keys = iws.keySet().toArray(new String[iws.keySet().size()]);
		if (keys != null){
			for(String key : keys){
				_closeIndexWriter(iws.get(key));
			}
			iws = null;
		}
		
		logger.info("\t IndexWriters closed");
		
		logger.info(">> Closing IndexSearcher");		
		_closeIndexSearcher();
		logger.info("\t IndexSearcher closed");		
	

		if (ir != null){
			logger.info(">> Closing IndexReader");			
			_closeIndexReader();
			logger.info("\t IndexReader closed");			
		}
		
		
		if (initConfig.getMultiLanguageIndex()){
			this.merge(true);
		}
		logger.info("***JFTSTool - Session close correctly***");
		sessionStarted = false;
	}

	/**
	 * Closes a session JFTSTool
	 * This involves closing the indexSearcher and all IndexWriters.
	 * By default it performs the merge of the auxiliary index
	 * if you are using multilanguage support and also
	 * optimizes the main index at the end.
	 * @throws JFTSFrameworkException
	 */	
	public void closeSession() throws JFTSFrameworkException {
		this.closeSession(true, true);
	}

	
	/**
	 * Close JFTS session when a error occurs
	 */
	private void _closeSessionWithError(){
		logger.error("Closing JFTS session. Fatal error ocurrs");		
		
		//close indexWriters
		String[] keys = iws.keySet().toArray(new String[iws.keySet().size()]);
		if (keys != null){
			for(String key : keys){
				_closeIndexWriter(iws.get(key));
			}
		}
		
		//close indexSearcher
		_closeIndexSearcher();
	}
	
	
	/**
	 * Get main configuration
	 * @return JFTSConfiguration
	 * @throws JFTSConfigException
	 */
	public JFTSConfiguration getJFTSConfiguration() throws JFTSConfigException {
		return JFTSConfiguration.getInstance();
	}

	/**
	 * Set config file path
	 * @param path
	 */
	public void setConfigPath(String path){
		configPath = path;
	}
	
	/**
	 * Set config log file path 
	 * @param path
	 */
	public void setLogPath(String path){
		logPath = path;
	}
	
	/**
	 * Get the IndexReader
	 * @return IndexReader
	 * @throws JFTSFrameworkException
	 */
	public IndexReader getIR() throws JFTSFrameworkException {
		if (ir == null){
			_initializeIR();
		}
		return ir;
		
	}

	/**
	 * Get the IndexSearcher
	 * @return Searcher
	 * @throws JFTSFrameworkException
	 */
	public Searcher getIS() throws JFTSFrameworkException {
		if (is == null){
			_initializeIS();
		}
		return is;
	}
	
	/**
	 * Get map of IndexWriters
	 * @return Map of IndexWriters 
	 * @throws JFTSFrameworkException
	 */
	public Map<String, IndexWriter> getIWS() throws JFTSFrameworkException {
		if (iws == null){
			_initializeIWS();
		}
		return iws;
	}
	

	/**
	 * </ p> will do the merge of the auxiliary indices by language and
	 * the main index (In case you are using support
	 * multilanguage. </ P>
	 * <p> <b> Perform this action involves jfts log today. </b> </ p>
	 * @param optimize Whether optimizes main index after
	 * made the merge. </ P>
	 * @throws JFTSFrameworkException
	 */
	public void merge(Boolean optimize) throws JFTSFrameworkException {
		logger.info(">> Starting to merge indexes");		
		String indexBasePath = null;
		JFTSAnalyzer analyzer = null;
		AnalyzerManager mgr = null;	
		String[] languages = null;
		IndexWriter baseIndex = null;		
		try {
			//Open base index
			indexBasePath = config.getIndexerConfig().getIndexPath();
			int maxFieldLength = config.getIndexerConfig().getMaxFieldLength();
			MaxFieldLength mfl = new MaxFieldLength(maxFieldLength);
			Directory dir = FSDirectory.getDirectory(indexBasePath);
			mgr = new AnalyzerManager();
			analyzer = _getAnalyzer(mgr.getDefaultLanguage());			
			baseIndex = new IndexWriter(dir, analyzer, mfl);
			
			logger.info("\t Open base index: " + indexBasePath);			
			
			languages = config.getSupportedLanguages();			
			String pathAux = null;
			for(String l : languages){
				pathAux = indexBasePath + '\\' + l;
				logger.info("\t merge aux index: " + pathAux);				
				baseIndex.addIndexesNoOptimize( new Directory[] {FSDirectory.getDirectory(pathAux)} );
			}
			
			if(optimize){
				logger.info("\t Optimize index");				
				baseIndex.optimize();
			}

			logger.info("\t Close base index");			
			baseIndex.close();
			
			
			logger.info("\t Delete aux index");			
			//Delete temp index directories
			File indexDir = new File(indexBasePath);
			File auxFile = null;
			String[] files = indexDir.list();
			if (files != null){
				for (int i = 0; i < files.length; i++){
					auxFile = new File(indexDir, files[i]);
					if (auxFile.isDirectory()){
						_deleteDirectory(auxFile);
					}
				}
			}
			logger.info(">> Finish merge proccess");			
		}
		catch (Exception e) {
			e.printStackTrace();
			throw new JFTSFrameworkException(e.getMessage());
		}
		finally{
			try {
				baseIndex.close();
			}
			catch (Exception e1) { /*do nothing */ } 
		}
	}

	/**
	 * Deletes a directory and recursively all its contents 
	 * @param file Directory to delete
	 */
	private void _deleteDirectory(File file){
		if (file.isDirectory()){
			String[] files = file.list();
			for(int i =0; i < files.length; i++){
				_deleteDirectory(new File(file, files[i]));
			}
			file.delete();
		}
		else{
			file.delete();
		}
	}

	
	/**
	 * Close a indexWriter
	 * @param iw IndexWriter to close
	 */
	private void _closeIndexWriter(final IndexWriter iw){
		if (iw != null){
			try {
				iw.close();
			}
			catch (Exception e) { /* do nothing */}
		}
	}

	/**
	 * Close a indexSearcher
	 */
	private void _closeIndexSearcher(){
		if (is != null){
			try {
				is.close();
				is = null;
			}
			catch (Exception e) { /* do nothing */}
		}
	}

	/**
	 * Close a IndexReader
	 */
	private void _closeIndexReader(){
		if (ir != null){
			try {
				ir.close();
				ir = null;
			}
			catch (Exception e) { /* do nothing */}
		}
	}	
	
	
	/**
	 * <p>Initialize indexWriter Map of JFTS that depending on whether multi-language set to indexed or not.
	 * If JFTS supports multi language, creates an IndexWriter for each supported language. </p>
	 * <p> Also creates a temporary index for each language, which at the end of the session
	 * merge is performed incorporating these indices with the principal.
	 * If JFTS not support multi-language, creates an IndexWriter for the default language </p>
	 * @throws JFTSFrameworkException
	 */
	private void _initializeIWS() throws JFTSFrameworkException{
		String[] languages = null;
		JFTSAnalyzer analyzer = null;
		String indexBasePath = null;
		try {
			if (initConfig.getMultiLanguageIndex()){
				languages = config.getSupportedLanguages();
				
				if (languages != null){
					//Create lucene index
					indexBasePath = config.getIndexerConfig().getIndexPath();
					int maxFieldLength = config.getIndexerConfig().getMaxFieldLength();
					MaxFieldLength mfl = new MaxFieldLength(maxFieldLength);
					Directory dir = null;
					
					for (String l : languages){
						dir = FSDirectory.getDirectory(indexBasePath + '\\' + l);					
						analyzer = _getAnalyzer(l);
						iws.put(l, new IndexWriter(dir, analyzer, true, mfl));					
					}
				}
				else{
					throw new JFTSFrameworkException("Error, can't find any language to build indexWriter");
				}
			}
			else{
				AnalyzerManager mgr = null;
				try {
					mgr = new AnalyzerManager();
					String language = mgr.getDefaultLanguage();
					
					//Create lucene index
					indexBasePath = config.getIndexerConfig().getIndexPath();
					int maxFieldLength = config.getIndexerConfig().getMaxFieldLength();
					MaxFieldLength mfl = new MaxFieldLength(maxFieldLength);
					Directory dir = FSDirectory.getDirectory(indexBasePath);
					
					analyzer = _getAnalyzer(language);
					iws.put(language, new IndexWriter(dir, analyzer, mfl));					
				}
				catch (AnalyzerManagerException e) {
					throw new JFTSFrameworkException(e.getMessage());
				}				
			}
		}
		catch (JFTSConfigException e) {
			throw new JFTSFrameworkException(e.getMessage());			
		}
		catch(IOException e){
			throw new JFTSFrameworkException(e.getMessage());			
		}
	}

	
	/**
	 * Initialize the indexSearcher
	 * @throws JFTSFrameworkException
	 */
	private void _initializeIS() throws JFTSFrameworkException {
		String indexPath = null;
		try {
			indexPath = config.getIndexerConfig().getIndexPath();
			Directory dir = FSDirectory.getDirectory(indexPath);
			is = new IndexSearcher(dir);	
			
		}
		catch (JFTSConfigException e) {
			throw new JFTSFrameworkException(e.getMessage());
		}
		catch (IOException e) {
			throw new JFTSFrameworkException(e.getMessage());			
		}
	}
	
	/**
	 * Initialize the indexReader
	 * @throws JFTSFrameworkException
	 */
	private void _initializeIR() throws JFTSFrameworkException {
		String indexPath = null;
		try {
			indexPath = config.getIndexerConfig().getIndexPath();
			Directory dir = FSDirectory.getDirectory(indexPath);
			ir = IndexReader.open(dir);	
			
		}
		catch (JFTSConfigException e) {
			throw new JFTSFrameworkException(e.getMessage());
		}
		catch (IOException e) {
			throw new JFTSFrameworkException(e.getMessage());			
		}
	}	
	

	/**
	 * Returns the corresponding JFTSAnalyzer language that receives as parameter.
	 * If the language parameter is received by UNKNOWN and is configured to JFTS
	 * use a default Analyzer, Analyzer returns the corresponding language
	 * by default.
	 * @param language
	 * @return Returns a subclass of the abstract class JFTSAnalyzer
	 * @throws JFTSFrameworkException	 */
	private JFTSAnalyzer _getAnalyzer(String language) throws JFTSFrameworkException {
		JFTSAnalyzer analyzer = null;
		try {
			String unsupportedLanguage = config.getLanguageDetectorConfig().getUnknownLanguage();
			AnalyzerManager dinamicAnalyzer = new AnalyzerManager();

			if (language.trim().equalsIgnoreCase(unsupportedLanguage.trim())){
				if (config.getIndexerConfig().getUseDefaultAnalyzer()){
					language = dinamicAnalyzer.getDefaultLanguage();
				}
			}
			analyzer = dinamicAnalyzer.getAnalyzer(language);
		}
		catch (Exception e) {
			_closeSessionWithError();
			throw new JFTSFrameworkException(e.getMessage());			
		}
		return analyzer;
	}


	@Override
	public void addDBDocuments(String useConnection, String dbQuery,
			boolean ignoreErrors)
			throws JFTSIndexerException, JFTSFrameworkException {
		try{
			
			if (iws == null){
				iws = new HashMap<String, IndexWriter>();				
				_initializeIWS();
				indexer.setIndexWriters(iws);
			}

			indexer.addDBDocuments(useConnection, dbQuery, ignoreErrors);
		}
		catch(JFTSIndexerException e){
			_closeSessionWithError();
			throw e;
		}
	}


	@Override
	public void addDocument(File file) throws JFTSIndexerException, JFTSFrameworkException {
		try{
			if (iws == null){
				_initializeIWS();
				indexer.setIndexWriters(iws);
			}
			
			indexer.addDocument(file);
		}
		catch(JFTSIndexerException e){
			_closeSessionWithError();
			throw e;
		}		
	}


	@Override
	public void addDocument(File file, boolean optimizeIndex)
			throws JFTSIndexerException, JFTSFrameworkException {
		try{
			if (iws == null){
				iws = new HashMap<String, IndexWriter>();				
				_initializeIWS();
				indexer.setIndexWriters(iws);
			}
			
			indexer.addDocument(file, optimizeIndex);
		}
		catch(JFTSIndexerException e){
			_closeSessionWithError();
			throw e;
		}		
		
	}


	@Override
	public void addDocuments(File directory, boolean ignoreErrors)
			throws JFTSIndexerException, JFTSFrameworkException {
		try{
			if (iws == null){
				iws = new HashMap<String, IndexWriter>();				
				_initializeIWS();
				indexer.setIndexWriters(iws);
			}
			
			indexer.addDocuments(directory, ignoreErrors);
		}
		catch(JFTSIndexerException e){
			_closeSessionWithError();
			throw e;
		}				
	}
	
	@Override
	public long deleteDocument(String field, String id)
			throws JFTSIndexerException, JFTSFrameworkException {
		try{
			if (ir == null){
				_initializeIR();
				indexer.setIndexReader(ir);
			}
			return indexer.deleteDocument(field, id);
		}
		catch(JFTSIndexerException e){
			_closeSessionWithError();
			throw e;
		}		
	}


	@Override
	public JFTSResult prefixQuerySearch(String field, String query, ResultOrder order, int page, int resultsPerPage)
			throws JFTSSearcherException, JFTSFrameworkException {
		
		try{
			if (is == null){
				_initializeIS();
				searcher.setIndexSearcher(is);
			}

			if (ir == null){
				_initializeIR();
				searcher.setIndexReader(ir);
			}			
			
			return searcher.prefixQuerySearch(field, query, order, page, resultsPerPage);
		}
		catch(JFTSSearcherException e){
			_closeSessionWithError();
			throw e;
		}				
	}


	@Override
	public JFTSResult simpleQuerySearch(String field, String query,
			ResultOrder order, int page, int resultsPerPage)
			throws JFTSSearcherException, JFTSFrameworkException {
		
		try{
			if (is == null){
				_initializeIS();
				searcher.setIndexSearcher(is);
			}
			
			if (ir == null){
				_initializeIR();
				searcher.setIndexReader(ir);
			}			
			
			return searcher.simpleQuerySearch(field, query, order, page, resultsPerPage);
		}
		catch(JFTSSearcherException e){
			_closeSessionWithError();
			throw e;
		}		
	}


	@Override
	public Document getDocument(int id) throws JFTSSearcherException,
			JFTSFrameworkException {

		try{
			if (is == null){
				throw new JFTSFrameworkException("Error, searcher is't initialized");
			}
			else{
				return searcher.getDocument(id);
			}
		}
		catch(JFTSSearcherException e){
			_closeSessionWithError();
			throw e;
		}				
	}


	@Override
	public ResultOrder getValidOrders() throws JFTSSearcherException,
			JFTSFrameworkException {
		return searcher.getValidOrders();
	}


	@Override
	public JFTSResult humanQuerySearch(String query, ResultOrder order, int page, int resultsPerPage, String language) throws JFTSSearcherException,
			JFTSFrameworkException {
		
		try{
			if (is == null){
				_initializeIS();
				searcher.setIndexSearcher(is);
			}

			if (ir == null){
				_initializeIR();
				searcher.setIndexReader(ir);
			}			
			
			return searcher.humanQuerySearch(query, order, page, resultsPerPage, language);
		}
		catch(JFTSSearcherException e){
			_closeSessionWithError();
			throw e;
		}	
	}


	@Override
	public JFTSResult phraseQuerySearch(String field, String[] phrase, int slop,
			ResultOrder order, int page, int resultsPerPage) throws JFTSSearcherException,
			JFTSFrameworkException {
		
		try{
			if (is == null){
				_initializeIS();
				searcher.setIndexSearcher(is);
			}
			
			if (ir == null){
				_initializeIR();
				searcher.setIndexReader(ir);
			}			
			
			return searcher.phraseQuerySearch(field, phrase, slop, order, page, resultsPerPage);
		}
		catch(JFTSSearcherException e){
			_closeSessionWithError();
			throw e;
		}		
		

	}


	@Override
	public JFTSResult phraseQuerySearch(String field, String phrase, String regEx,
			int slop, ResultOrder order, int page, int resultsPerPage) throws JFTSSearcherException,
			JFTSFrameworkException {
		
		try{
			if (is == null){
				_initializeIS();
				searcher.setIndexSearcher(is);
			}
			
			if (ir == null){
				_initializeIR();
				searcher.setIndexReader(ir);
			}			
			
			return searcher.phraseQuerySearch(field, phrase, regEx, slop, order, page, resultsPerPage);
		}
		catch(JFTSSearcherException e){
			_closeSessionWithError();
			throw e;
		}		
		
	}


	@Override
	public JFTSResult rangeQuerySearch(String field, String begin, String end,
			boolean inclusive, ResultOrder order, int page, int resultsPerPage) throws JFTSSearcherException,
			JFTSFrameworkException {
		
		try{
			if (is == null){
				_initializeIS();
				searcher.setIndexSearcher(is);
			}
			
			if (ir == null){
				_initializeIR();
				searcher.setIndexReader(ir);
			}			
			
			return searcher.rangeQuerySearch(field, begin, end, inclusive, order, page, resultsPerPage);
		}
		catch(JFTSSearcherException e){
			_closeSessionWithError();
			throw e;
		}		
		
		
	}


	@Override
	public JFTSResult similarityQuerySearch(String field, String query,
			ResultOrder order, int page, int resultsPerPage) throws JFTSSearcherException,
			JFTSFrameworkException {
		
		try{
			if (is == null){
				_initializeIS();
				searcher.setIndexSearcher(is);
			}
			
			if (ir == null){
				_initializeIR();
				searcher.setIndexReader(ir);
			}			
			
			return searcher.similarityQuerySearch(field, query, order, page, resultsPerPage);
		}
		catch(JFTSSearcherException e){
			_closeSessionWithError();
			throw e;
		}		
		
	}


	@Override
	public JFTSResult wildCardQuerySearch(String field, String query,
			ResultOrder order, int page, int resultsPerPage) throws JFTSSearcherException,
			JFTSFrameworkException {
		
		try{
			if (is == null){
				_initializeIS();
				searcher.setIndexSearcher(is);
			}
			
			if (ir == null){
				_initializeIR();
				searcher.setIndexReader(ir);
			}			
			
			return searcher.wildCardQuerySearch(field, query, order, page, resultsPerPage);
		}
		catch(JFTSSearcherException e){
			_closeSessionWithError();
			throw e;
		}		
	}
	
	@Override
	public TopDocs moreLikeThisQuery(int docId, String[] fields,
			ResultOrder order) throws JFTSSearcherException,
			JFTSFrameworkException {
		
		try{
			if (is == null){
				_initializeIS();
				searcher.setIndexSearcher(is);
			}
			
			if (ir == null){
				_initializeIR();
				searcher.setIndexReader(ir);
			}			
			
			return searcher.moreLikeThisQuery(docId, fields, order);
		}
		catch(JFTSSearcherException e){
			_closeSessionWithError();
			throw e;
		}		
	}	


	@Override
	public JFTSHierarchy getOntoCategorization() throws JFTSSearcherException,
			JFTSFrameworkException {

		try{
			if (is == null){
				_initializeIS();
				searcher.setIndexSearcher(is);
			}
			
			if (ir == null){
				_initializeIR();
				searcher.setIndexReader(ir);
			}			
		
			return searcher.getOntoCategorization();
		}
		catch(JFTSSearcherException e){
			_closeSessionWithError();
			throw e;
		}		
	}

	/**
	 * Return true if JFTS session is started
	 * @return boolean
	 */
	public boolean isSessionStarted(){
		return sessionStarted;
	}
	

	@Override
	public String getOntClassPath(String className)
			throws JFTSSearcherException, JFTSFrameworkException {
		return searcher.getOntClassPath(className);
	}
}